package cn.apestar.common.chat.exception;

import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BaseException extends RuntimeException implements Serializable {
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    private Exception exception;
    private Logger log;
    private TYPE exceptionType;
    private String userMessage = "";
    private String code = "0";
    private String errorMessage = "";
    private Object keyObject = null;
    private boolean isExistKeyObject = false;
    private String message = null;

    public BaseException(Exception exception, Logger log) {
        this.exception = exception;
        this.log = log;

        if ((exception instanceof BaseException)) {
            formatBaseException();
            writeTraceErrorLog();
        } else {
            writeSysErrorLog();
        }
    }

    public BaseException(Exception exception, Logger log, Object keyObject) {
        this.exception = exception;
        this.log = log;
        this.keyObject = keyObject;
        this.isExistKeyObject = true;

        if ((exception instanceof BaseException)) {
            formatBaseException();
            writeTraceErrorLog();
        } else {
            writeSysErrorLog();
        }
    }

    public BaseException(String code, Logger log) {
        this.code = code;
        this.log = log;
        writeAppErrorLog();
    }

    public BaseException(Exception exception, String code, Logger log) {
        this.exception = exception;
        this.code = code;
        this.log = log;
        if ((exception instanceof BaseException)) {
            formatBaseException();
            writeTraceErrorLog();
        } else {
            writeSysErrorLog();
        }
    }

    public BaseException(String code, Logger log, Object keyObject) {
        this.code = code;
        this.log = log;
        this.keyObject = keyObject;
        this.isExistKeyObject = true;
        writeAppErrorLog();
    }

    public BaseException(Exception exception, String code, Logger log, Object keyObject) {
        this.exception = exception;
        this.code = code;
        this.log = log;
        this.keyObject = keyObject;
        this.isExistKeyObject = true;
        if ((exception instanceof BaseException)) {
            formatBaseException();
            writeTraceErrorLog();
        } else {
            writeSysErrorLog();
        }
    }

    public BaseException(String message) {
        this.message = message;
        this.exceptionType = TYPE.BIZ;
    }

    public BaseException(String code, String message) {
        this.message = message;
        this.code = code;
        this.exceptionType = TYPE.BIZ;
    }

    private void formatBaseException() {
        this.message = this.exception.getMessage();
    }

    public void formatBaseException(Throwable t) {
        this.message = t.getMessage();
    }

    private void writeSysErrorLog() {
        try {
            String exceptionStackTraceInfo = getExceptionStackTraceInfo();

            StackTraceElement stackTraceElement = getStackTrace()[0];
            String errorInfo = "";
            if (this.isExistKeyObject) {
                errorInfo = "方法[" + stackTraceElement.getMethodName() + "],代码[" + this.code + "],异常[" + this.errorMessage + "],关键数据[{}]";
                this.log.error(errorInfo + exceptionStackTraceInfo, this.keyObject);
            } else {
                errorInfo = "方法[" + stackTraceElement.getMethodName() + "],代码[" + this.code + "],异常[" + this.errorMessage + "]";
                this.log.error(errorInfo + exceptionStackTraceInfo);
            }
        } catch (Exception e) {
            this.logger.error(e.getMessage());
        }
    }

    private void writeAppErrorLog() {
        try {
            StackTraceElement stackTraceElement = getStackTrace()[0];
            String errorInfo = "";
            if (this.isExistKeyObject) {
                errorInfo = "方法[" + stackTraceElement.getMethodName() + "],代码[" + this.code + "],异常[" + this.errorMessage + "],关键数据[{}]";
                this.log.error(errorInfo, this.keyObject);
            } else {
                errorInfo = "方法[" + stackTraceElement.getMethodName() + "],代码[" + this.code + "],异常[" + this.errorMessage + "]";
                this.log.error(errorInfo);
            }
        } catch (Exception e) {
            this.logger.error(e.getMessage());
        }
    }

    private void writeTraceErrorLog() {
        try {
            StackTraceElement stackTraceElement = getStackTrace()[0];
            String errorInfo = "方法[" + stackTraceElement.getMethodName() + "]";
            this.log.error(errorInfo);
        } catch (Exception e) {
            this.logger.error(e.getMessage());
        }
    }

    @Override
    public String getMessage() {
        if (this.message == null) {
            this.message = (this.userMessage + "[" + this.code + "],请与管理员联系");
        }
        return this.message;
    }

    public String getErrorCode() {
        return this.code;
    }

    public TYPE getExceptionType() {
        return this.exceptionType;
    }

    private String getExceptionStackTraceInfo() {
        String exceptionTrace = "";
        try {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            this.exception.printStackTrace(pw);
            exceptionTrace = "\r\n" + sw.toString();
            pw.flush();
            pw.close();
            sw.flush();
            sw.close();
        } catch (Exception e) {
            this.logger.error(e.getMessage());
        }
        return exceptionTrace;
    }

    static enum TYPE {
        DATABASE, SYSTEM, APPLY, BIZ, RULE;
    }
}